Range Utility
As we saw a couple of lessons ago, we can use .map
to iterate over an array of data, to render 1 React element per item.
But what if we don't have an array?
For example, it's common for ratings to use a 5-star system. Let's suppose we want to render 0 to 5 little star icons, depending on the rating.
Spend a few minutes poking at the problem here. Your goal is to render n
stars inside the StarRating
component, where n
is the value of rating
:
Code Playground
This is a tricky problem!
Our default tool in JavaScript to do this sort of thing would be a for
loop. As we've learned, though, for
is a statement, and we can't use statements within our JSX.
Here's one solution: we can use a for
loop above the JSX, to create our array of elements:
function StarRating({ rating }) { let stars = [];
for (let i = 0; i < rating; i++) { stars.push( <img key={i} alt="" className="gold-star" src="https://sandpack-bundler.vercel.app/img/gold-star.svg" /> ); }
return ( <div className="star-wrapper"> {stars} </div> );}
We create an array of image elements with a for
loop, and then we render that array inside our JSX. As we saw with .map
, React can “unpack” arrays for us, and render each of the elements inside, so long as we provide a unique key
for each element.
A functional alternative
There's nothing wrong with this approach, but I have a preferred alternative: using a range
function.
range
is a utility function. It's not part of the JavaScript language (unfortunately), but it is a staple of utility libraries like lodash.
This is one of my favourite utilities. I use it in every project I work on (in fact, I've used it over 20 times in the codebase for this course platform!). I love it.
To understand how it works, let's look at some examples:
// Create an array from 0 (inclusive) to 2 (exclusive):range(2);// Produces: [0, 1]
// Create an array from 0 (inclusive) to 5 (exclusive):range(5);// Produces: [0, 1, 2, 3, 4]
// Create an array from 2 (inclusive) to 6 (exclusive):range(2, 6);// Produces: [2, 3, 4, 5]
// Create an array from 2 to 10, picking every 2nd numberrange(2, 10, 2);// Produces: [2, 4, 6, 8]
Essentially, it's the expression version of a “for” loop statement, like how &&
can be an expression version of an “if” statement. This means we can use it in our JSX.
Here's how we'd use it:
function StarRating({ rating }) { return ( <div className="star-wrapper"> {range(rating).map((num) => ( <img key={num} alt="" className="gold-star" src="https://sandpack-bundler.vercel.app/img/gold-star.svg" /> ))} </div> );}
range(rating)
will produce an array from 0 up to (but not including) n
, where n
is the supplied rating. Then, we use the .map
trick we learned about to iterate over that array, creating a copy of our star image for each one.
For the key
, we use the number generated within the array, since we know these numbers will be unique.
Range function code
Here's how the range
function is defined:
const range = (start, end, step = 1) => { let output = [];
if (typeof end === 'undefined') { end = start; start = 0; }
for (let i = start; i < end; i += step) { output.push(i); }
return output;};
Here's a complete solution demonstrating this function:
Code Playground
When relevant, this range
function will be provided to you on this course platform.
In terms of using this in the real world, I like to create a utils.js
file that collects this and other handy JS functions. You can also use the lodash “range” function from NPM.